// ==============================================================
//
//  Copyright (c) 1999-2002 by Alex Vinokur.  This work and all works
//  derived from it may be copied and modified without any
//  restrictions other than that a copy of this copyright notice
//  must be included in any copy of this work or any derived work.
//
// ==============================================================
static char id [] = "@(#)## n-ary Huffman Template Algorithm ## Author : Alex Vinokur ## "__FILE__;

// ##############################################################
// =================================
//  n-ary Huffman Template Algorithm
// ==================================
//
//  FILE : huf_main.cpp
//
//  AUTHOR : Alex Vinokur
//
//  DESCRIPTION :
//
//
//         Running the tests
//         -----------------
//
// ##############################################################


// ===================
#include "methods.h"
// ===================


// #####################################################
#ifndef ARITY
#define	ARITY 	2
#endif


// #####################################################
#define	ARGC_DATA_FILE		4
#define	ARGC_WEIGHT_FILE	3

// #####################################################
static	map<string, string>	symbol_types_gs;
static	map<string, string>	weight_types_gs;

// #####################################################
// ===================
#define	ADD_SYMBOL_TYPE(s)	symbol_types_gs[typeid(s).name()] = #s	
void fill_symbol_types ()
{
	ADD_SYMBOL_TYPE(char);
	ADD_SYMBOL_TYPE(string);
}


// ===================
#define	ADD_WEIGHT_TYPE(w)	weight_types_gs[typeid(w).name()] = #w	
void fill_weight_types ()
{
	ADD_WEIGHT_TYPE(short);
	ADD_WEIGHT_TYPE(int);
	ADD_WEIGHT_TYPE(long);
	ADD_WEIGHT_TYPE(float);
	ADD_WEIGHT_TYPE(double);
}


// ===================
void show_help (
	int argc, 
	char **argv, 
	const string& msg_i = string(),
	const string& illegal_value_i = string()
	)
{
map<string, string>::const_iterator  cur_const_iter;

	if (!msg_i.empty())
	{
		cout << endl;
		cout << "\t### " << msg_i;
		if (!illegal_value_i.empty())
		{
			cout << " : " << illegal_value_i;
		}
		cout << " ###" << endl;
	}

	cout << endl;
	cout << "USAGE : " << endl;

	cout << "   " 
	     << argv[0] 
	     << " "
	     << "<symbol-type>" 
	     << " "
	     << "<weight-type>" 
	     << " "
	     << "<data-file-name>" 
	     << endl;

	cout << "   " 
	     << argv[0] 
	     << " "
	     << "<weight-type>" 
	     << " "
	     << "<weights-file-name>" 
	     << endl;

	cout << "   " 
	     << setw (string (argv[0]).size()) 
	     << "" 
	     << " * "
	     << "symbol-type : "; 

	for (cur_const_iter = symbol_types_gs.begin(); 
	     !(cur_const_iter == symbol_types_gs.end()); 
	     cur_const_iter++
	    )
	{
		cout << (*cur_const_iter).second << " ";
	}
	cout << endl;

	cout << "   " 
	     << setw (string (argv[0]).size()) 
	     << "" 
	     << " * "
	     << "weight-type : "; 

	for (cur_const_iter = weight_types_gs.begin(); 
	     !(cur_const_iter == weight_types_gs.end()); 
	     cur_const_iter++
	    )
	{
		cout << (*cur_const_iter).second << " ";
	}
	cout << endl;

	assert (ARITY >= 2);

	cout << "   " 
	     << setw (string (argv[0]).size()) 
	     << "" 
	     << " * NOTE! "
	     << "arity = " 
	     << ARITY;

	switch (ARITY)
	{
		case 2 :
			cout << " (i.e. Binary Huffman Tree)";
			break;

		case 3 :
			cout << " (i.e. Ternary Huffman Tree)";
			break;

		default :
			cout << " (i.e. " << ARITY << "-ary Huffman Tree)";
			break;
	}
	cout << endl; 
}


// ===================
template <typename SYMBOL, typename WEIGHT>
void build_loaded_tree (const string& data_file_name_i)
{
LoadedHuffmanTree<SYMBOL, WEIGHT, ARITY>   the_tree (data_file_name_i);
	show_file ("Data", data_file_name_i);

ostringstream osstr;
	osstr << "Creating Loaded ";	

	assert (ARITY >=2 );
	switch (ARITY)
	{
		case 2 :
			osstr << "Binary";	
			break;

		case 4 :
			osstr << "Ternary";	
			break;

		default :
			osstr << ARITY << "-ary";	
			break;
	}

	osstr << " Huffman Tree from <";	

	assert (symbol_types_gs.count (typeid(SYMBOL).name()) == 1);
	osstr << symbol_types_gs[typeid(SYMBOL).name()];	

	osstr << ", ";	

	assert (weight_types_gs.count (typeid(WEIGHT).name()) == 1);
	osstr << weight_types_gs[typeid(WEIGHT).name()];	

	osstr << ">-data file";	

        the_tree.showAll (osstr.str());

}

// ===================
template <typename WEIGHT>
void build_dried_tree (const string& weights_file_name_i)
{
DriedHuffmanTree<WEIGHT, ARITY>   the_tree (weights_file_name_i);
	show_file ("Weights", weights_file_name_i);

ostringstream osstr;
	osstr << "Creating Dried ";	

	assert (ARITY >=2 );
	switch (ARITY)
	{
		case 2 :
			osstr << "Binary";	
			break;

		case 4 :
			osstr << "Ternary";	
			break;

		default :
			osstr << ARITY << "-ary";	
			break;
	}

	osstr << " Huffman Tree from <";	
	assert (weight_types_gs.count (typeid(WEIGHT).name()) == 1);
	osstr << weight_types_gs[typeid(WEIGHT).name()];	
	osstr << ">-weights file";	

        the_tree.showAll (osstr.str());

}

// #####################################################
// ==============================
#define	IF_DATA_TYPES(s, w)	\
	{	    		\
		if ( 		\
		     (string(argv[symbol_index]) == string(#s))	\
		     && 	\
		     (string(argv[weihgt_index]) == string(#w))	\
		    ) 		\
		{     		\
			types_flag = true; 		\
			build_loaded_tree<s, w>(argv[file_name_index]);	\
		}		\
	}
	
// ==============================
#define	IF_WEIGHT_TYPE(w)	\
	{		\
		if ( 		\
		     string(argv[weihgt_index]) == string(#w)	\
		    ) 		\
		{     		\
			types_flag = true; 	\
			build_dried_tree<w>(argv[file_name_index]);	\
		}		\
	}



// #####################################################
// ############# main ##################################
// #####################################################
// ==============================
int main (int argc, char **argv)
{
int	symbol_index = 0;
int	weihgt_index = 0;
int	file_name_index = 0;
bool	arvc_flag = false;
bool	types_flag = false;

map<string, string>::const_iterator  cur_const_iter;

	// ---------------------------
	assert (ARITY >= 2);

	// ---------------------------
	cout << endl;
	cout << endl;
	cout << "\t------> YOUR COMMAND LINE : ";
	for (int i = 0; i < argc; i++)
	{
		cout <<	argv[i] << " ";
	}
	cout << endl;

	// ---------------------------
	fill_symbol_types ();
	fill_weight_types ();

	// ---------------------------
	if (argc == ARGC_DATA_FILE)
	{
		arvc_flag = true;
		symbol_index = 1;
		weihgt_index = 2; 
	}

	if (argc == ARGC_WEIGHT_FILE)
	{
		arvc_flag = true;
		weihgt_index = 1;
	}


	if (arvc_flag)
	{
       		file_name_index = weihgt_index + 1;
	}
	else
	{
		show_help (argc, argv, "Illegal number of arguments");
		return 1;
	}
	assert (weihgt_index >= 1);
	assert (file_name_index == (weihgt_index + 1));

	// ---------------------------------
string	checked_value;
bool	checking_flag;

	// --------------------
	if (symbol_index)
	{
		checked_value = argv[symbol_index];
		checking_flag = false;
		for (cur_const_iter = symbol_types_gs.begin(); 
		     !(cur_const_iter == symbol_types_gs.end()); 
		     cur_const_iter++
		    )
		{
			if ((*cur_const_iter).second == checked_value)
			{
				checking_flag = true;
				break;
			}
		}
		if (!checking_flag)
		{
			show_help (argc, argv, "Illegal symbol type", checked_value);
			return 1;
		}
	} // if (symbol_index)

	// --------------------
	checked_value = argv[weihgt_index];
	checking_flag = false;
	for (cur_const_iter = weight_types_gs.begin(); 
	     !(cur_const_iter == weight_types_gs.end()); 
	     cur_const_iter++
	    )
	{
		if ((*cur_const_iter).second == checked_value)
		{
			checking_flag = true;
			break;
		}
	}
	if (!checking_flag)
	{
		show_help (argc, argv, "Illegal weight type", checked_value);
		return 1;
	}

	// --------------------
ostringstream osstr1;
ostringstream osstr2;
ostringstream osstr3;

	if (symbol_index)
	{
		osstr1 << "Loaded";
	}
	else
	{
		osstr1 << "Dried";
	}
	osstr1 << " ";

	switch (ARITY)
	{
		case 2 :
			osstr1 << "Binary";
			break;

		case 3 :
			osstr1 << "Ternary";
			break;

		default :
			osstr1 << ARITY << "-ary";
			break;
	}
	osstr1 << " Huffman Tree";

	if (symbol_index)
	{
		osstr2 << "Symbol type : ";	
		osstr2 << argv[symbol_index];	
	}
	osstr3 << "Weight type : ";	
	osstr3 << argv[weihgt_index];	


int	max_show_size = 0;

	max_show_size = MAX_VALUE (max_show_size, osstr1.str().size());
	max_show_size = MAX_VALUE (max_show_size, osstr2.str().size());
	max_show_size = MAX_VALUE (max_show_size, osstr3.str().size());
	max_show_size += 4;

	cout << endl;
	cout << "\t" << string (max_show_size , '#') << endl;
	cout << "\t" << "### " << osstr1.str() << endl;
	if (symbol_index)
	{
		cout << "\t" << "### " << osstr2.str() << endl;
	}
	cout << "\t" << "### " << osstr3.str() << endl;
	cout << "\t" << string (max_show_size, '#') << endl;
	cout << endl;



	// --------------------
	// --------------------
	// --------------------
	if (argc == ARGC_DATA_FILE)
	{
		assert (symbol_index);
		assert (weihgt_index);

		IF_DATA_TYPES(char, short);
		IF_DATA_TYPES(char, int);
		IF_DATA_TYPES(char, long);
		IF_DATA_TYPES(char, float);
		IF_DATA_TYPES(char, double);

		IF_DATA_TYPES(string, short);
		IF_DATA_TYPES(string, int);
		IF_DATA_TYPES(string, long);
		IF_DATA_TYPES(string, float);
		IF_DATA_TYPES(string, double);

	}

	if (argc == ARGC_WEIGHT_FILE)
	{
		assert (!symbol_index);
		assert (weihgt_index);

		IF_WEIGHT_TYPE(short);
		IF_WEIGHT_TYPE(int);
		IF_WEIGHT_TYPE(long);
		IF_WEIGHT_TYPE(float);
		IF_WEIGHT_TYPE(double);

	}

	assert (types_flag);

} // main


// #######################################################
// ################ END OF FILE ##########################
// #######################################################

